package org.windowkit.toolkit;

import java.awt.Component;
import java.awt.Container;
import java.awt.FocusTraversalPolicy;
import java.awt.Point;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

import javax.swing.LayoutFocusTraversalPolicy;

/**
 * A very basic focus traversal policy.
 * The traversal is from left to right, top to bottom using
 * absoulte screen coordinates.  All component hierarchy is
 * ignored.
 * <p>
 * This is usefull if you have created a window with mulitple
 * different embedded panels and you need to satisfy SUBMEPPS
 * tabbing requirement.
 * <p>
 * I wrote this really quickly for my needs, it may need to be
 * modified to be more robust.
 *
 * <p>
 * <table border>
 * <tr><th colspan = 3>History</th></tr>
 * <tr>
 * <th>Date</th><th>Developer</th><th>Description</th>
 * </tr>
 * <tr>
 * <td>1/22/06</td>
 * <td>Rich O'Connell</td>
 * <td>Cleaned up code.</td>
 * </tr>
 * </table>
 *
 * @author Rich O'Connell
 *
 */


public class AbsoluteLocationFocusTraversalPolicy extends FocusTraversalPolicy {

    private static final FitnessTestFTP fitnessTestPolicy =
        new FitnessTestFTP();

    public AbsoluteLocationFocusTraversalPolicy() {
        super();
    }


    @SuppressWarnings("unchecked")
    @Override
    public Component getComponentAfter(final Container aContainer, final Component aComponent) {

        Component[] comps = aContainer.getComponents();

        ArrayList<Component> focusableComps = getFocusableComponents(comps);

        Collections.sort(focusableComps, new MyComponentComparator());

        int indx = focusableComps.indexOf(aComponent);
        if(indx != -1){
            if(indx + 1 >= focusableComps.size()){
                return focusableComps.get(0);
            }
            return focusableComps.get(indx + 1);

        }
        return null;

    }

    @SuppressWarnings("unchecked")
    @Override
    public Component getComponentBefore(final Container aContainer, final Component aComponent) {
        Component[] comps = aContainer.getComponents();

        ArrayList<Component> focusableComps = getFocusableComponents(comps);

        Collections.sort(focusableComps, new MyComponentComparator());

        int indx = focusableComps.indexOf(aComponent);
        if(indx != -1){
            if(indx - 1 < 0){
                return focusableComps.get(focusableComps.size() - 1);
            }
            return focusableComps.get(indx - 1);

        }
        return null;
    }

    @SuppressWarnings("unchecked")
    @Override
    public Component getFirstComponent(final Container aContainer) {
        Component[] comps = aContainer.getComponents();

        ArrayList<Component> focusableComps = getFocusableComponents(comps);

        Collections.sort(focusableComps, new MyComponentComparator());

        //find the first component that isnt a tab
//        for(int i = 0; i < focusableComps.size(); i++){
//            if(!(focusableComps.get(i) instanceof JTabbedPane)){
//                return focusableComps.get(i);
//            }
//        }

        if(focusableComps.size() > 0){
            return focusableComps.get(0);
        }

        return null;
    }

    @SuppressWarnings("unchecked")
    @Override
    public Component getLastComponent(final Container aContainer) {
        Component[] comps = aContainer.getComponents();

        ArrayList<Component> focusableComps = getFocusableComponents(comps);

        Collections.sort(focusableComps, new MyComponentComparator());

        //find the first component that isnt a tab
//        for(int i = 0; i < focusableComps.size(); i++){
//            if(!(focusableComps.get(i) instanceof JTabbedPane)){
//                return focusableComps.get(i);
//            }
//        }
        if(focusableComps.size() > 0){
            return focusableComps.get(focusableComps.size() - 1);
        }

        return null;
    }

    @Override
    public Component getDefaultComponent(final Container aContainer) {
        return getFirstComponent(aContainer);
    }

    @SuppressWarnings("unchecked")
    private ArrayList<Component> getFocusableComponents(final Component[] comps) {
        ArrayList<Component> compList = new ArrayList<Component>();
        getFocusableComponents(comps, compList);
        return compList;
    }


    private void getFocusableComponents(final Component[] comps, final ArrayList<Component> compList){

        for (Component comp : comps) {
            if(comp instanceof Container){
                getFocusableComponents(((Container)comp).getComponents(), compList);
            }

            if(accept(comp)){
                compList.add(comp);
            }
        }
    }


    public boolean accept(final Component c){
        if(!fitnessTestPolicy.accept(c)) {
			return false;
		}

        if(!c.isShowing()) {
			return false;
		}

        return true;
    }


    public class MyComponentComparator implements Comparator{

        /**Sometimes components in the same row will
        have slightly different y coordinates due
        to being embedded in different panels.
        Adjust this variable if needed. **/
        private int ROW_TOLERANCE;

        public MyComponentComparator(){
            this(15);
        }
        public MyComponentComparator(final int rowTolerance){
            ROW_TOLERANCE = rowTolerance;
        }

        public void setRowTolerance(final int r){
            ROW_TOLERANCE = r;
        }

        public int compare(final Object o1, final Object o2) {

            Component a = (Component)o1;
            Component b = (Component)o2;

            Point aLoc = a.getLocationOnScreen();
            Point bLoc = b.getLocationOnScreen();

            int ax = aLoc.x, ay = aLoc.y, bx = bLoc.x, by = bLoc.y;

            int result = 0;

            if(Math.abs(ay - by) < ROW_TOLERANCE){
                //same y
                if(ax < bx){
                    result = -1;
                }else if (ax > bx){
                    result = 1;
                }else{
                    result = 0;
                }
            }else{
                if(ay < by){
                    result = -1;
                }else{
                    result = 1;
                }
            }
//            System.out.println("Comparing: " + aLoc.x + "," + aLoc.y + " to " + bLoc.x + "," + bLoc.y + " result = " + result);

            return result;
        }
    }

    /* extend the LayoutFocusTraversalPolicy so we can make accept public */
    static class FitnessTestFTP extends LayoutFocusTraversalPolicy{
        /**
		 *
		 */
		private static final long serialVersionUID = 5742330030209214820L;

		@Override
        public boolean accept(final Component aComponent) {
            return super.accept(aComponent);
        }
    }

}
